﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

using Noftware.Com.Cryptography;

namespace Noftware.Com.Cryptographer.UI
{
    /// <summary>
    /// Main form.
    /// </summary>
    public partial class Main : Form
    {
        #region Private variables

        /// <summary>
        /// Cryptography settings.
        /// </summary>
        private CryptographySettings _package = new CryptographySettings();

        /// <summary>
        /// Generator class.
        /// </summary>
        private Generator _gen = new Generator();

        #endregion

        #region Constructors

        /// <summary>
        /// Default constructor.
        /// </summary>
        public Main()
        {
            InitializeComponent();

            // Set the initial value of the salt requirement
            _package.Mode = Mode.Encryption;
            _package.CryptographyType = CryptographyType.Md5;
        }

        #endregion

        /// <summary>
        /// Set the salt requirement.
        /// </summary>
        /// <param name="type">Cryptography type.</param>
        private void setPackage(CryptographyType type)
        {
            // Set the value of the salt requirement
            _package.CryptographyType = type;
            _package.Input = this.txtInput.Text;
            txtOutput.Text = string.Empty;

            // Set the controls
            _package.Mode = Mode.Encryption;
            rdoEncrypt.Checked = true;

            // Set controls based on salt requirement
            switch (_package.SaltRequirement)
            {
                case SaltRequirement.Required:
                    {
                        lblSalt.Text = "&Salt:";
                        txtSaltKey.Enabled = true;
                        cmdSaltPopup.Enabled = true;
                        break;
                    }
                case SaltRequirement.NotPermitted:
                    {
                        lblSalt.Text = "&Salt: (not required)";
                        txtSaltKey.Enabled = false;
                        txtSaltKey.Text = string.Empty;
                        cmdSaltPopup.Enabled = false;
                        break;
                    }
                case SaltRequirement.RequiredIntegerOnly:
                    {
                        lblSalt.Text = "&Salt: (integer only)";
                        txtSaltKey.Enabled = true;
                        cmdSaltPopup.Enabled = true;
                        try
                        {
                            // Check to see if it is a numeric key already
                            txtSaltKey.Text = (int.Parse(txtSaltKey.Text)).ToString();
                        }
                        catch (Exception)
                        {
                            // Suppress exception, key is non-numeric
                            txtSaltKey.Text = string.Empty;
                        }
                        break;
                    }
                default:
                    break;
            }

            this.setGenerateButton();
        }

        /// <summary>
        /// MD5 radio-button event handler.
        /// </summary>
        private void rdoMd5_CheckedChanged(object sender, EventArgs e)
        {
            setPackage(CryptographyType.Md5);
        }

        /// <summary>
        /// Rijndael radio-button event handler.
        /// </summary>
        private void rdoRijndael_CheckedChanged(object sender, EventArgs e)
        {
            setPackage(CryptographyType.Rijndael);
        }

        /// <summary>
        /// XOR radio-button event handler.
        /// </summary>
        private void rdoXor_CheckedChanged(object sender, EventArgs e)
        {
            setPackage(CryptographyType.Xor);
        }

        /// <summary>
        /// Hex radio-button event handler.
        /// </summary>
        private void rdoHex_CheckedChanged(object sender, EventArgs e)
        {
            setPackage(CryptographyType.Hex);
        }

        /// <summary>
        /// Event handler called upon page load.
        /// </summary>
        private void Main_Load(object sender, EventArgs e)
        {
            this.txtSaltKey.KeyDown += this.txtSaltKey_KeyDown;
            setPackage(CryptographyType.Md5);
        }

        /// <summary>
        /// Salt popup button event handler.
        /// </summary>
        private void cmdSaltPopup_Click(object sender, EventArgs e)
        {
            // Set and show the Generator form
            var gen = new KeyGenerator();
            gen.NumericOnly = (_package.SaltRequirement == SaltRequirement.RequiredIntegerOnly);
            gen.ShowDialog(this);

            // Was a key specified?
            if (gen.KeyValue.Length > 0)
            {
                this.txtSaltKey.Text = gen.KeyValue;
            }
            gen.Close();
        }

        /// <summary>
        /// Event handler of the Encryption mode radio button.
        /// </summary>
        private void rdoEncrypt_CheckedChanged(object sender, EventArgs e)
        {
            _package.Mode = Mode.Encryption;
            lblInput.Text = "&Text to be encrypted:";
            lblOutput.Text = "En&crypted text:";
            cmdGenerate.Text = "Encr&ypt";

            // Don't further execute if the Encrypt button is being deselected
            if (rdoEncrypt.Checked == false)
            {
                return;
            }

            // Set the input and clear the output
            txtInput.Text = txtOutput.Text;
            txtOutput.Text = string.Empty;
        }

        /// <summary>
        /// Event handler of the Decryption mode radio button.
        /// </summary>
        private void rdoDecrypt_CheckedChanged(object sender, EventArgs e)
        {
            _package.Mode = Mode.Decryption;
            lblInput.Text = "&Text to be decrypted:";
            lblOutput.Text = "De&crypted text:";
            cmdGenerate.Text = "Decr&ypt";

            // Don't further execute if the Decrypt button is being deselected
            if (rdoDecrypt.Checked == false)
            {
                return;
            }

            // Set the input and clear the output
            txtInput.Text = txtOutput.Text;
            txtOutput.Text = string.Empty;
        }

        /// <summary>
        /// Set the Enabled state of the Generate button.
        /// </summary>
        private void setGenerateButton()
        {
            if (txtInput.Text.Length > 0)
            {
                switch (_package.SaltRequirement)
                {
                    case SaltRequirement.Required:
                        {
                            if (txtSaltKey.Text.Length == 0)
                            {
                                // No salt
                                cmdGenerate.Enabled = false;
                            }
                            else
                            {
                                // Salt is specified
                                cmdGenerate.Enabled = true;
                                _package.StringKey = txtSaltKey.Text;
                                _package.NumericKey = 0;
                            }

                            break;
                        }
                    case SaltRequirement.NotPermitted:
                        {
                            cmdGenerate.Enabled = true;
                            _package.StringKey = null;
                            _package.NumericKey = 0;
                            break;
                        }
                    case SaltRequirement.RequiredIntegerOnly:
                        {
                            if (txtSaltKey.Text.Length == 0)
                            {
                                // No salt
                                cmdGenerate.Enabled = false;
                            }
                            else
                            {
                                // Salt is specified
                                cmdGenerate.Enabled = true;
                                _package.StringKey = null;
                                _package.NumericKey = 0;
                            }

                            break;
                        }
                    default:
                        break;
                } // switch (_package.SaltRequirement)
            }
            else
            {
                // No input
                cmdGenerate.Enabled = false;
            }

            _package.Input = txtInput.Text;
        }

        /// <summary>
        /// Event handler of text input.
        /// </summary>
        private void txtInput_TextChanged(object sender, EventArgs e)
        {
            this.setGenerateButton();
        }

        /// <summary>
        /// Event handler of text input.
        /// </summary>
        private void txtSaltKey_TextChanged(object sender, EventArgs e)
        {
            this.setGenerateButton();
        }

        /// <summary>
        /// Event handler of Salt input to strip non-numeric character for numeric-only salts.
        /// </summary>
        private void txtSaltKey_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
        {
            if (_package.SaltRequirement == SaltRequirement.RequiredIntegerOnly)
            {
                // Navigation and backspace are permitted
                switch (e.KeyCode)
                {
                    case Keys.Left:
                    case Keys.Right:
                    case Keys.Up:
                    case Keys.Down:
                    case Keys.PageDown:
                    case Keys.PageUp:
                    case Keys.Back:
                    case Keys.Tab:
                        return;
                }

                int result = 0;
                char c = (char)e.KeyValue;
                if (int.TryParse(c.ToString(), out result) == false)
                {
                    e.SuppressKeyPress = true;
                    e.Handled = true;
                }
            } // if (_package.SaltRequirement == SaltRequirement.RequiredIntegerOnly)
        }

        /// <summary>
        /// Event handler of the generate button.
        /// </summary>
        private void cmdGenerate_Click(object sender, EventArgs e)
        {
            switch (_package.SaltRequirement)
            {
                case SaltRequirement.Required:
                    {
                        _package.StringKey = txtSaltKey.Text;
                        _package.NumericKey = 0;
                        break;
                    }
                case SaltRequirement.NotPermitted:
                    {
                        _package.StringKey = null;
                        _package.NumericKey = 0;
                        break;
                    }
                case SaltRequirement.RequiredIntegerOnly:
                    {
                        _package.StringKey = null;
                        try
                        {
                            _package.NumericKey = int.Parse(txtSaltKey.Text);
                        }
                        catch (OverflowException)
                        {
                            // int is exceeded
                            cmdGenerate.Enabled = false;
                            _package.NumericKey = 0;
                            MessageBox.Show(this, string.Format("For XOR, the maximum value permitted is {0}.", int.MaxValue), "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                            return;
                        }
                        break;
                    }
                default:
                    break;
            } // switch (_package.SaltRequirement)

            try
            {
                txtOutput.Text = _gen.Generate(_package);
            }
            catch (Exception ex)
            {
                string formatedError = string.Format("The {0} {1} failed. Reason: {2}", _package.CryptographyType, _package.Mode, (ex.InnerException == null ? ex.Message : ex.InnerException.Message));
                MessageBox.Show(this, formatedError, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        /// <summary>
        /// Event handler of the Reset button.
        /// </summary>
        private void cmdClear_Click(object sender, EventArgs e)
        {
            txtInput.Text = string.Empty;
            txtOutput.Text = string.Empty;
            txtSaltKey.Text = string.Empty;

            rdoEncrypt.Checked = true;
            rdoMd5.Checked = true;

            this.setGenerateButton();
        }

        /// <summary>
        /// Event handler of the About menu.
        /// </summary>
        private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            var about = new About();
            about.ShowDialog(this);
        }

        /// <summary>
        /// Event handler of the Exit menu.
        /// </summary>
        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.Close();
        }
    }
}
